import SwiftSyntax
import SwiftSyntaxMacros

/// A type representing data associated with an enum-case declarations.
///
/// This type informs how this variable needs to be initialized,
/// decoded/encoded in the macro expansion phase.
protocol EnumCaseVariable: ConditionalVariable, NamedVariable
where CodingLocation == EnumCaseCodingLocation, Generated == EnumCaseGenerated {
    /// All the associated variables for this case.
    ///
    /// Represents all the associated variable data available in this
    /// enum-case declaration data.
    var variables: [any AssociatedVariable] { get }
}

extension EnumCaseVariable {
    /// The arguments passed to encoding condition.
    ///
    /// All associated variables are passed as argument.
    var conditionArguments: LabeledExprListSyntax {
        LabeledExprListSyntax {
            for variable in self.variables {
                variable.conditionArguments
            }
        }
    }
}

/// Represents the location for decoding/encoding for `EnumCaseVariable`s.
///
/// Represents the container and value for `EnumCaseVariable`s
/// decoding/encoding.
struct EnumCaseCodingLocation {
    /// The decoder/encoder for decoding
    /// or encoding enum-case content.
    ///
    /// This decoder/encoder is used for decoding
    /// or encoding enum-case associated variables.
    let coder: TokenSyntax
    /// The values of the variable.
    ///
    /// Represents the actual values that will be decoded.
    /// Only the first value will be encoded.
    let values: [ExprSyntax]
}

/// Represents the syntax generated by `EnumCaseVariable`.
///
/// Represents the switch case label with additional code generated.
struct EnumCaseGenerated {
    /// The switch case label.
    ///
    /// This label will be used to represent decoding/encoding
    /// for a particular enum-case variable.
    let label: SwitchCaseLabelSyntax
    /// The enum-case decoding/encoding syntax.
    ///
    /// This contains syntax for container retrieval and conditional
    /// syntax based on container availability.
    let code: PropertyVariableTreeNode.Generated
}

extension EnumCaseGenerated: ConditionalVariableSyntax {
    /// Generates new syntax with provided condition.
    ///
    /// Adds condition to where clause of the switch case.
    ///
    /// - Parameter condition: The condition for the existing syntax.
    /// - Returns: The new syntax.
    func adding(condition: LabeledExprListSyntax) -> EnumCaseGenerated {
        let condition: ExprSyntax = "(\(condition))"
        let label = SwitchCaseLabelSyntax {
            for item in self.label.caseItems {
                let clauseExpr =
                    if let previousClause = item.whereClause {
                        "\(condition) && \(previousClause.condition)"
                            as ExprSyntax
                    } else {
                        condition
                    }
                let clause = WhereClauseSyntax(condition: clauseExpr)
                SwitchCaseItemSyntax(pattern: item.pattern, whereClause: clause)
            }
        }
        return .init(label: label, code: code)
    }
}
